refactor(web): network connection form UX (part 6)#3353
Merged
dgdavid merged 2 commits intoenhance-network-connection-formfrom Apr 7, 2026
Merged
refactor(web): network connection form UX (part 6)#3353dgdavid merged 2 commits intoenhance-network-connection-formfrom
dgdavid merged 2 commits intoenhance-network-connection-formfrom
Conversation
By adding a `sync` prop to `DeviceSelector` that registers a TanStack Form listener to write a derived value into a second field whenever a device is selected.
34217e7 to
d7801c4
Compare
This comment was marked as resolved.
This comment was marked as resolved.
e70dc8e to
21448ad
Compare
imobachgs
approved these changes
Apr 7, 2026
dgdavid
added a commit
that referenced
this pull request
Apr 8, 2026
This is the final PR of series of changes for reimplementing the network connection form using [TanStack Form](https://tanstack.com/form/). Built on top of #3353, it removes the old IP settings form and its dependencies, completes the field component set, improves the name auto-sync approach, and fixes a validation gap in advanced mode. ### Removal of IpSettingsForm `IpSettingsForm` and its supporting components (`AddressesDataList`, `DnsDataList`, `DnsSearchDataList`, `IpAddressInput`, `IpPrefixInput`, and their tests) are deleted. The new `ConnectionForm` based on TanStack Form fully replaces them. ### Bug fix: address validation in advanced mode Addresses are optional in advanced mode, but if entered they must still be valid. Previously only manual mode validated them, so invalid entries could silently reach the server. The fix ensures to also validate entries when they are present in advanced mode, even though none are required. ### `generateConnectionName` as a pure function `useConnectionName` was a React hook that had no real React dependency: it computed a name from its arguments and returned a string. It has been replaced by `generateConnectionName`, a plain function in `src/utils/network.ts` that is easier to test and works anywhere without a component context. ### Name auto-sync via form-level listeners The previous approach used `useStore` to watch binding fields and a `useEffect` to write the derived name back. Both are replaced by TanStack Form's own [`listeners` API](https://tanstack.com/form/latest/docs/framework/react/guides/listeners)), which is the idiomatic way to handle field side effects in this library. The listener fires on mount and on every change and, as in previous approach, it skips the update when the name field is already dirty, [using `isDirty` rather than `isTouched`](https://tanstack.com/form/latest/docs/framework/react/guides/basic-concepts#field-state) so that focusing and blurring the field without typing does not stop auto-generation. [`dontRunListeners: true`](https://github.com/TanStack/form/blob/42761767cf0662559d4f2c1a51b45b3720dbd451/packages/form-core/src/FieldApi.ts#L1446-L1449) prevents the name write from re-triggering the listener. ### DropdownField rename `ChoiceField` was renamed to `DropdownField` since the old name gave no hint about what kind of input it renders. The TSDoc now explains that the component uses a PatternFly menu-based combobox rather than a native `<select>`, why the keyboard interaction differs from what users may expect, and includes a TODO for implementing the W3C-recommended arrow-key compromise. ### TextField and CheckboxField Two new field components following the same `useFieldContext` pattern as `DropdownField` and `ArrayField`: - `TextField`: a text input that shows validation errors inline below the input. - `CheckboxField`: a checkbox with an optional description. Both are registered in `useAppForm` and wired into `ConnectionForm` (name field, DNS toggles) and `IpSettings` (gateway field), replacing the inline markup that was previously scattered across those components. A `## Field component conventions` section was added to `form-contexts.ts` to document the shared contract: `onChange` is wired internally, `onBlur` is intentionally not wired (submit-only validation, revisit when a use case arises), and lifecycle events belong on `form.AppField` via `listeners`, not on the component itself. ### Action button form components Two action button components are registered under `formComponents` so they are available on any typed form instance without form-specific wiring: - `form.SubmitButton`: reads `isSubmitting` via `useFormContext` and renders a submit button that shows a loading indicator and disables itself while the form is submitting. - `form.CancelButton`: encapsulates `navigate(-1)` via `useNavigate`, rendering a link button that returns to the previous page. `ConnectionForm`'s action group reduces to: ```tsx <ActionGroup> <form.SubmitButton /> <form.CancelButton /> </ActionGroup> ``` ### Form conventions update `src/components/form/conventions.md` has been updated to reflect the implemented state of `ConnectionForm`. Stale notes are replaced with accurate descriptions, conditional rendering is consistently described as "not rendered" rather than "hidden" throughout, and a new Validation section explains the submit-only approach, the cross-field `onSubmitAsync` pattern, and the `setErrorMap` workaround needed to re-enable submission after a failed attempt.
Merged
imobachgs
added a commit
that referenced
this pull request
Apr 14, 2026
Prepare to release version 20. * #3294 * #3295 * #3296 * #3297 * #3298 * #3299 * #3300 * #3301 * #3302 * #3303 * #3304 * #3305 * #3306 * #3307 * #3308 * #3309 * #3310 * #3311 * #3312 * #3313 * #3315 * #3316 * #3317 * #3318 * #3319 * #3320 * #3322 * #3323 * #3325 * #3326 * #3327 * #3329 * #3330 * #3331 * #3333 * #3334 * #3336 * #3338 * #3339 * #3342 * #3343 * #3349 * #3351 * #3352 * #3353 * #3354 * #3356 * #3357 * #3358 * #3359 * #3360 * #3361 * #3362 * #3363 * #3364 * #3365 * #3366 * #3367 * #3368 * #3371 * #3372 * #3373 * #3375 * #3376 * #3378 * #3379 * #3380 * #3381 * #3382 * #3385 * #3386
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Built on top of #3352, this PR fixes a small but noticeable UX gap found while recording the screencast for the referenced PR: when switching between "Chosen by name" and "Chosen by MAC" binding modes, the device selection was lost and the user had to re-pick it.
To fix it, one of the components has gained the ability to trigger listeners on demand, making it possible to wire the two different selectors instantiated under the hood and keep them in sync. Note that although only one instance is mounted at a time and there is no real risk of listener loops,
dontRunListeners: trueis still passed to thesetFieldValuecall inside the listener as a safety net.network-device-selectors-in-sync.mp4
For more details, check the changes.
Related to:
setFieldValueTanStack/form#1680Notes for reviewers